<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>移动端拖拽demo</title>
    <style>
        body {
            margin: 0;
        }

        div {
            display: flex;
            flex-direction: column;
        }
    
        .blank-cell-group,
        .char-cell-group {
            width: 100%;
            flex-direction: row;
        }
    
        .blank-cell-group .cell-item,
        .char-cell-group .cell-item {
            width: 25%;
            height: 25vw;
            padding: .5rem;
            box-sizing: border-box;
        }
    
        .char-cell-group {
            flex-wrap: wrap;
            margin-top: 5rem;
        }
    
        .blank-cell-group .cell-item .wrapper,
        .char-cell-group .cell-item .wrapper {
            width: 100%;
            height: 100%;
            border: .1rem solid #ddd;
            box-sizing: border-box;
        }
    
        .char-cell-group .cell-item .wrapper {
            border: none;
            background-color: rgb(189, 28, 189);
            font-size: 3rem;
            justify-content: center;
            align-items: center;
            color: #fff;
        }
    </style>
    <script>
        document.documentElement.style.fontSize = '10px';
    </script>
</head>

<body>
    <div id="app">
        <div class="container">
            <div class="blank-cell-group">
                <div class="cell-item">
                    <div class="wrapper"></div>
                </div>
                <div class="cell-item">
                    <div class="wrapper"></div>
                </div>
                <div class="cell-item">
                    <div class="wrapper"></div>
                </div>
                <div class="cell-item">
                    <div class="wrapper"></div>
                </div>
            </div>

            <div class="char-cell-group">
            </div>
        </div>
    </div>
    <script>
        (() => {
            const idioms = ['诗情画意', '南来北往', '一团和气', '落花流水'];
            const oCharCellGroup = document.querySelector('.char-cell-group')
            const oBlanks = document.querySelectorAll('.blank-cell-group .wrapper');

            let charCollection = [];
            let charAreas = [];
            let blankAreas = [];
            let resArr = [];
            let oChars = null;
            // 鼠标距左距上的距离
            let startX = 0;
            let startY = 0;
            // 单元格距左距上的距离
            let cellX = 0;
            let cellY = 0;
            // 单元格的高度与宽度
            let cellH = 0;
            let cellW = 0;
            // 鼠标距离元素边框的距离 (固定)
            let mouseX = 0;
            let mouseY = 0;

            const init = () => {
                charCollection = formatCharsArr();
                // 渲染所有的单元格
                render();
                oChars = oCharCellGroup.querySelectorAll('.cell-item .wrapper');

                getAreas(oBlanks, blankAreas);

                // 获取每一个单元格的坐标
                getAreas(oChars, charAreas)
                // 绑定事件
                bindEvent();
            }

            function render() {
                let list = '';
                charCollection.forEach((char, index) => {
                    list += charCellTpl(char, index);
                });
                oCharCellGroup.innerHTML = list;
            }

            function bindEvent () {
                let oChar = null;   
                for (let i = 0; i < oChars.length; i ++) {
                    oChar = oChars[i];

                    oChar.addEventListener('touchstart', handleTouchStart, false);
                    oChar.addEventListener('touchmove', handleTouchMove, false);
                    oChar.addEventListener('touchend', handleTouchEnd, false);
                }
            }

            function remHandler(value) {
                return value / 10 + 'rem';
            }

            function handleTouchStart (e) {
                cellW = this.offsetWidth;
                cellH = this.offsetHeight;

                cellX = this.offsetLeft;
                cellY = this.offsetTop;

                startX = e.touches[0].clientX;
                startY = e.touches[0].clientY;

                mouseX = startX - cellX;
                mouseY = startY - cellY;

                this.style.width = remHandler(cellW);
                this.style.height = remHandler(cellH);
                this.style.position = 'fixed';
                setPosition(this, { x: cellX, y: cellY })
            }

            function handleTouchMove(e) {
                e.preventDefault();
                const moveX = e.touches[0].clientX,
                      moveY = e.touches[0].clientY;
                cellY = moveY - mouseY;
                cellX = moveX - mouseX;
                setPosition(this, { x: cellX, y: cellY })
            }

            function handleTouchEnd(e) {
                const blankWidth = oBlanks[0].offsetWidth;
                const blankHeight = oBlanks[0].offsetHeight;

                for(let i = 0; i < blankAreas.length; i ++) {
                    if (resArr[i] !== undefined) {
                        continue;
                    }

                    // 空白处的距左距上距离
                    let { startX, startY } = blankAreas[i];

                    // 判断单元格左、右顶点超过空白元素半个的条件
                    if (
                        (
                            cellX >= startX &&
                            cellX < startX + blankWidth / 2 &&
                            cellY < startY + blankHeight / 2
                        )
                        ||
                        (
                            cellX + cellW > startX + blankWidth / 2 &&
                            cellX + cellW <= startX + blankWidth &&
                            cellY < startY + blankHeight / 2
                        )
                    ) {
                        setPosition(this, { x: startX, y: startY });
                        setResArr(this, i);
                        if (!resArr.includes(undefined) && resArr.length === 4) {
                            setTimeout(() => {
                                if (!checkResult()) {
                                    alert('错了');
                                } else {
                                    alert('正确了');
                                }
                                resetPosition();
                            }, 1000);
                        }
                        return;
                    }
                }
                
                // 获取属性data-index
                const _index = Number(this.dataset.index);
                const charArea = charAreas[_index];
                setPosition(this, { x: charArea.startX, y: charArea.startY })
            }

            function getAreas(domCollection, itemWrapper) {
                let startX = 0,
                    stratY = 0,
                    temp = null;

                for (let i = 0; i < domCollection.length; i ++) {
                    temp = domCollection[i];
                    startX = temp.offsetLeft;
                    startY = temp.offsetTop;
                    itemWrapper.push({
                        startY,
                        startX,
                    })
                }
            }

            function formatCharsArr() {
                let _arr = [];
                idioms.forEach(item => {
                    _arr = _arr.concat(item.split(''));
                })
                return _arr.sort(randomSort);
            }

            function randomSort(a, b) {
                return Math.random() > 0.5 ? 1 : -1;
            }

            function charCellTpl(char, index) {
                return (
                    `<div class="cell-item">
                        <div class="wrapper" data-index="${ index }">${ char }</div>
                    </div>`
                );
            }

            function setPosition(el, { x, y}) {
                el.style.left = remHandler(x);
                el.style.top = remHandler(y);
            }

            function setResArr(el, index) {
                resArr[index] = {
                    char: el.innerText,
                    el,
                }
            }

            function resetPosition() {
                resArr.forEach(item => {
                    const el = item.el,
                          index = Number(el.dataset.index),
                          { startX, startY } = charAreas[index];
                    setPosition(el, { x: startX, y: startY });
                })
                resArr = [];
                startX = 0;
                startY = 0;
                cellX = 0;
                cellY = 0;
                cellH = 0;
                cellW = 0;
                mouseX = 0;
                mouseY = 0;
            }

            function checkResult() {
                let idiom = '';
                resArr.forEach(item => {
                    idiom += item.char;
                })
                return idioms.find(item => item === idiom);
            }

            init();
        })();
    </script>
</body>

</html>